/*
 * Copyright (c) 2023-2024 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.screw.engine;

import static org.elsfs.cloud.screw.constant.ScrewConstants.DEFAULT_ENCODING;
import static org.elsfs.cloud.screw.engine.EngineTemplateType.velocity;

import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.log.NullLogChute;
import org.elsfs.cloud.common.util.exception.ProduceException;
import org.elsfs.cloud.common.util.lang.Assert;
import org.elsfs.cloud.common.util.lang.ExceptionUtils;
import org.elsfs.cloud.common.util.lang.StringUtils;
import org.elsfs.cloud.screw.metadata.model.DataModel;

/**
 * velocity template
 *
 * @author SanLi Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2020/3/17 21:40
 */
public class VelocityTemplateEngine extends AbstractTemplateEngine {
  /** DATA */
  private static final String DATA = "_data";

  /**
   * 构造函数
   *
   * @param templateConfig {@link EngineConfig }
   */
  public VelocityTemplateEngine(EngineConfig templateConfig) {
    super(templateConfig);
  }

  /** VelocityEngine */
  private static VelocityEngine velocityEngine;

  {
    // 初始化模板引擎
    velocityEngine = new VelocityEngine();
    // 如果存在自定义模板
    if (StringUtils.isNotBlank(getEngineConfig().getCustomTemplate())) {
      velocityEngine.setProperty(
          "string.resource.loader.class",
          "org.apache.velocity.runtime.resource.loader.StringResourceLoader");
    } else {
      velocityEngine.setProperty(
          "file.resource.loader.class",
          "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
    }
    velocityEngine.setProperty("runtime.log.logsystem.class", NullLogChute.class.getName());
    velocityEngine.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, "");
    velocityEngine.setProperty(Velocity.ENCODING_DEFAULT, DEFAULT_ENCODING);
    velocityEngine.setProperty(Velocity.INPUT_ENCODING, DEFAULT_ENCODING);
    velocityEngine.setProperty("file.resource.loader.unicode", "true");
  }

  /**
   * 生成文档
   *
   * @param info {@link DataModel}
   * @return ByteArrayOutputStream
   * @throws ProduceException ProduceException
   */
  @Override
  public ByteArrayOutputStream produce(DataModel info, String docName) throws ProduceException {
    Assert.notNull(info, "DataModel can not be empty!");
    Template template;
    try {
      // get template path
      String path = getEngineConfig().getCustomTemplate();
      // 如果自定义了模板
      if (StringUtils.isNotBlank(path)) {
        template = velocityEngine.getTemplate(path, DEFAULT_ENCODING);
      } else {
        // 没有自定义模板，使用核心包自带
        template =
            velocityEngine.getTemplate(
                velocity.getTemplateDir()
                    + getEngineConfig().getFileType().getTemplateNamePrefix()
                    + velocity.getSuffix(),
                DEFAULT_ENCODING);
      }
      // output
      try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
          OutputStreamWriter writer = new OutputStreamWriter(outputStream, DEFAULT_ENCODING);
          BufferedWriter sw = new BufferedWriter(writer)) {
        // put data
        VelocityContext context = new VelocityContext();
        context.put(DATA, info);
        // generate
        template.merge(context, sw);
        return outputStream;
      }
    } catch (IOException e) {
      throw ExceptionUtils.mpe(e);
    }
  }
}
